package com.derbysoft.defender.web;

/**
 * 该DTO用于规范请求的响应格式。当前后端开发分离后，便于开发人员相互交流。<br/>
 * 若对该规范有重大变更，请及时告知所有开发人员。请勿随意修改正在使用的code，防止正在使用的地方出错。<br/>
 * 不允许随意创建 HttpResponse 实例，必须使用该DTO中预定义的以new开头的方法生成相应情景下的响应类型。<br/>
 * <h1>已定义code列表</h1>
 * <ol>
 * <li>1000~1999: 服务器消息类，保留扩展</li>
 * <li>2000~2999: 成功
 * <ul>
 * <li>2000：成功</li>
 * </ul>
 * </li>
 * <li>3000~3999: 保留扩展</li>
 * <li>4000~4999：业务错误或者不合法信息，比如找不到酒店信息，找不到account，不符合竞价规则等
 * <ul>
 * <li>4000: 请求参数错误</li>
 * <li>4001: 处理或保存数据失败</li>
 * <li>4003: 身份验证失败</li>
 * </ul>
 * </li>
 * <li>5000~5999：服务器内部错误信息，比如NullPointerException等。
 * <ul>
 * <li>5000: Internal Server Error</li>
 * </ul>
 * </li>
 * </ol>
 * <br/>
 * <p/>
 * Reference doc: <a href="https://cloud.google.com/apis/design/errors">API Errors</a>
 *
 * @see org.springframework.http.HttpStatus
 * @see org.apache.commons.httpclient.HttpStatus
 */
public class HttpResponse {
    private int code;
    private String message;
    private Object data;

    /**
     * response code for current request. It is required and known.<br/>
     * 显示执行结果的状态码，其值必须来自于{@link HttpStatus#getCode()}。
     */
    public int getCode() {
        return code;
    }

    /**
     * response message for current request.<br/>
     * 执行结果信息，其值必须来自于{@link HttpStatus#getMessage()}。
     */
    public String getMessage() {
        return message;
    }

    /**
     * response data for current request.<br/>
     * 返回响应数据。
     * 当code < 2000时，理论上该字段没有值；
     * 当code >= 2000 and < 3000时，该字段存储业务数据，格式没有限制，由接收程序自己处理；
     * 当code >= 3000时，该字段存储错误信息，格式没有限制，由接收程序自己处理。
     */
    public Object getData() {
        return data;
    }

    /**
     * For usage of java reflection.
     */
    private HttpResponse() {
    }

    /**
     * HttpResponse instance can only be created by HttpResponseBuilder.
     */
    private HttpResponse(HttpResponseBuilder builder) {
        this.code = builder.httpStatus.getCode();
        this.message = builder.httpStatus.getMessage();
        this.data = builder.data;
    }

    /**
     * Used to collect parameters and create HttpResponse instance.
     */
    private static class HttpResponseBuilder {
        private final HttpStatus httpStatus;
        private Object data;

        HttpResponseBuilder(HttpStatus httpStatus) {
            if (httpStatus == null) {
                throw new IllegalArgumentException("httpStatus should not be null");
            }

            this.httpStatus = httpStatus;
        }

        public HttpResponseBuilder data(Object data) {
            this.data = data;
            return this;
        }

        public HttpResponse build() {
            return new HttpResponse(this);
        }
    }

    public static HttpResponse newSuccessResponse() {
        return new HttpResponseBuilder(HttpStatus.OK).build();
    }

    public static HttpResponse newSuccessResponse(Object businessData) {
        return new HttpResponseBuilder(HttpStatus.OK).data(businessData).build();
    }

    public static HttpResponse newParamErrorResponse() {
        return new HttpResponseBuilder(HttpStatus.PARAMETER_ERROR).build();
    }

    public static HttpResponse newParamErrorResponse(Object error) {
        return new HttpResponseBuilder(HttpStatus.PARAMETER_ERROR).data(error).build();
    }

    public static HttpResponse newOperationFailResponse() {
        return new HttpResponseBuilder(HttpStatus.OPERATION_FAIL).build();
    }

    public static HttpResponse newOperationFailResponse(Object error) {
        return new HttpResponseBuilder(HttpStatus.OPERATION_FAIL).data(error).build();
    }

    public static HttpResponse newAuthFailResponse() {
        return new HttpResponseBuilder(HttpStatus.FORBIDDEN).build();
    }

    public static HttpResponse newAuthFailResponse(Object error) {
        return new HttpResponseBuilder(HttpStatus.FORBIDDEN).data(error).build();
    }

    public static HttpResponse newServerErrorResponse() {
        return new HttpResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }

    public static HttpResponse newServerErrorResponse(Object error) {
        return new HttpResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR).data(error).build();
    }

    /**
     * @deprecated Please use {@link #newParamErrorResponse()} or {@link #newParamErrorResponse(Object)}
     */
    @Deprecated
    public static HttpResponse newParamErrorResponse(String message, Object data) {
        HttpResponse response = new HttpResponse();
        response.code = 4000;
        response.message = message;
        response.data = data;
        return response;
    }

    /**
     * @deprecated Please use {@link #newOperationFailResponse()} or {@link #newOperationFailResponse(Object)}
     */
    @Deprecated
    public static HttpResponse newOperationFailResponse(String message, Object data) {
        HttpResponse response = new HttpResponse();
        response.code = 4001;
        response.message = message;
        response.data = data;
        return response;
    }

    /**
     * @deprecated Please use {@link #newAuthFailResponse()} or {@link #newAuthFailResponse(Object)}
     */
    @Deprecated
    public static HttpResponse newAuthFailResponse(String message, Object data) {
        HttpResponse response = new HttpResponse();
        response.code = 4003;
        response.message = message;
        response.data = data;
        return response;
    }
}
